#include "sched_lotterySC.h"
#include <cstdlib>
#include <iostream>

using namespace std;

/**
  @Ejercicio 5.
  @Purpose: Implementar al menos la idea básica del algoritmo y la optimización de tickets compensatorios 
	    (compensation tickets)
 @Parameters:
      core    : cantidad de cores 
      quantum : ticks de cpu que puede utilizar una tarea
      semilla : semilla de la secuencia pseudoaleatoria 
  @return: void
*/
SchedLotterySC::SchedLotterySC(vector<int> argn) {
  // Lottery solo funciona con un core
  // cout << "Cores:    " << argn[0] << endl;
  // cout << "Quantum:  " << argn[1] << endl;
  // cout << "Seed:     " << argn[2] << endl;  
  this->quantum = argn[1];
  this->remainder = argn[1];
  this->tasksInStandBy.clear();
  srand(argn[2]);  
}

SchedLotterySC::~SchedLotterySC() {
}

void SchedLotterySC::load(int pid,int deadline) {
  
  task_lotterySC task;
  task.pid = pid;
  task.tickets = 1;
  task.block = false;
  this->tasksInStandBy.push_back(task);
  
}

void SchedLotterySC::load(int pid) {
  load(pid, 0);
}

void SchedLotterySC::setBlock(int pid2block, bool block) {
  vector< task_lotterySC >::iterator it;  
  for (it = this->tasksInStandBy.begin() ; it != this->tasksInStandBy.end() ; ++it) {
    if ((*it).pid == pid2block) {
	(*it).block = block;      
    }
  }
}

void SchedLotterySC::block(int pid) {  
  setBlock(pid, true);
}

void SchedLotterySC::unblock(int pid) {
  setBlock(pid, false);
}

void SchedLotterySC::removeTask(int remove_pid) {
  vector< task_lotterySC >::iterator it;  
  for (it = this->tasksInStandBy.begin() ; it != this->tasksInStandBy.end() ; ++it) {
      if ((*it).pid == remove_pid ) {
	this->tasksInStandBy.erase(it);
	break;
      }
  }
}

/** 
 * 
 * scheduler draws a random ticket to select the next process
 * 
 */
int SchedLotterySC::choseByDrawLottery(int cpu) {
    int total_tickets = 0;
    int task_chosen = IDLE_TASK;
    vector < task_lotterySC >::iterator it;
    
    for (it = this->tasksInStandBy.begin() ; it != this->tasksInStandBy.end(); it++) {
      if ((*it).pid != current_pid(cpu) && !(*it).block) {
	  total_tickets += (*it).tickets;	  
      }
    }
    
    if ((total_tickets) == 0)
	return task_chosen;
    
    int winner = rand() % (total_tickets);    
    int summation = 0;
    for (it = this->tasksInStandBy.begin() ; it != this->tasksInStandBy.end(); it++) {      
      if ((*it).pid != current_pid(cpu) && !(*it).block) {
	summation += (*it).tickets;
	if (summation > winner) {
	   // found the winner	   
	   task_chosen = (*it).pid;
	   break;
	}
      }      
    }
    return task_chosen;	
}

int SchedLotterySC::nextTask(int cpu) {
  this->remainder = this->quantum;
  return choseByDrawLottery(cpu);
}

int SchedLotterySC::tick(int cpu, const enum Motivo m) {  
  int taskToExecute = IDLE_TASK;  
  switch (m) {
    // EXIT: la tarea terminó (ejecution return)
    case EXIT:	
	removeTask(current_pid(cpu));
	taskToExecute = nextTask(cpu);
	break;
    // TICK: la tarea consumió todo el ciclo utilizando el CPU
    case TICK:	
	this->remainder--;
	// Si se terminó el quantum
	if (this->remainder == 0) {
	  // elijo otra tarea
	  taskToExecute = nextTask(cpu);	  
	} else {	  
	  taskToExecute = current_pid(cpu);
	}
	break;
    // BLOCK: la tarea ejecutó una llamada bloqueante o permaneció bloqueada durante el último ciclo.
    case BLOCK:
	this->remainder--;	
	
	block(current_pid(cpu));
	taskToExecute = nextTask(cpu);
	break;
  }
  return taskToExecute;
}
